#include "ThreadedPopulationEvaluator.h"

#include <algorithm>
using std::max;

#include "../../base.h"
#include "../../thread/AutoMutex.h"
#include "../../hardware/HardwareInfo.h"

#include "Individual.h"

namespace GA {

/*
	ThreadedPopulationEvaluator
*/

/*	public	*/

ThreadedPopulationEvaluator::ThreadedPopulationEvaluator(int nPopulation, Individual** population) :
	_nPopulation(nPopulation),
	_population(population),
	_populationIndex(0) {}

void ThreadedPopulationEvaluator::evaluate() {
	/* reset population index */
	_populationIndex = 0;

	int nCpus    = HardwareInfo::getInstance().getCpuCount();
//	int nThreads = nCpus + max(nCpus / 4, 1);
	int nThreads = 2 * nCpus;

	/* create and run threads */
	PopulationEvaluatorThread** threads = new PopulationEvaluatorThread*[nThreads];
	for(int i = 0; i != nThreads; ++i) {
		PopulationEvaluatorThread* thread = new PopulationEvaluatorThread(*this);
		thread->start();
		threads[i] = thread;
	}

	/* join */
	for(int i = 0; i != nThreads; ++i) {
		threads[i]->join();
	}

	/* delete threads */
	deleteall<PopulationEvaluatorThread>(threads, nThreads);
	delete[] threads;
}

Individual* ThreadedPopulationEvaluator::_getNextIndividual() {
	AutoMutex mutex(_populationMutex);

	if (_populationIndex == _nPopulation) {
		/* population end reached */
		return 0;
	} else {
		/* next individual */
		return _population[_populationIndex++];
	}
}

/*
	PopulationEvaluatorThread
*/

/*	public	*/

ThreadedPopulationEvaluator::PopulationEvaluatorThread::PopulationEvaluatorThread(ThreadedPopulationEvaluator& evaluator) :
	_evaluator(evaluator) {}

void ThreadedPopulationEvaluator::PopulationEvaluatorThread::run() {
	/* individual */
	Individual* individual = _evaluator._getNextIndividual();

	while(individual != 0) {
		/* the actual work */
		individual->evaluate();

		/* next individual */
		individual = _evaluator._getNextIndividual();
	}
}

} // GA

